package com.robaone.util;

import java.util.*;

import com.robaone.dbase.types.*;

import java.text.CharacterIterator;
import java.text.StringCharacterIterator;
/**
 * <pre>   Copyright Mar 21, 2012 Ansel Robateau
         http://www.robaone.com

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.</pre>
 * @author Ansel
 *
 */
public class LogParser {
  public static String DATE = "DATE";
  public static String START = "START";
  public static String END = "END";
  public static String LOG = "LOG";

  protected String buffer;
  protected Vector<Object> log;
  protected DateType m_date;
  protected TimeType m_start;
  protected TimeType m_stop;
  public LogParser() {
    this.buffer = new String();
    this.m_date = new DateType();
    this.m_start = new TimeType();
    this.m_stop = new TimeType();
  }

  public static Vector<Object> parseLog(String log) throws Exception {
    LogParser parser = new LogParser();
    parser.log = new Vector<Object>();
    Hashtable<Object,Object> entry = null;
    java.text.StringCharacterIterator iterator = new java.text.StringCharacterIterator(log);
    int state = 1;
    String buffer = "";
    for(char c = iterator.current();c != CharacterIterator.DONE;c = iterator.next()){
      switch(state){
        case 1:
         // Look for header
         if(parser.parseHeader(iterator) == true){
           // Save previous log
           if(entry != null){
             entry.put(LogParser.LOG,buffer.trim());
             buffer = "";
             //parser.log.add(entry);
           }else if(buffer.length() > 0){
             entry = new Hashtable<Object,Object>();
             entry.put(LogParser.LOG,buffer.trim());
             parser.m_date.setValue("");
             parser.m_start.setValue("");
             parser.m_stop.setValue("");
             entry.put(LogParser.DATE,parser.m_date);
             entry.put(LogParser.START,parser.m_start);
             entry.put(LogParser.END,parser.m_stop);
             parser.m_date = new DateType();
             parser.m_start = new TimeType();
             parser.m_stop = new TimeType();
             buffer = "";
             parser.log.add(entry);
           }
           // Save new log
           entry = new Hashtable<Object,Object>();
           entry.put(LogParser.DATE,parser.m_date);
           entry.put(LogParser.START,parser.m_start);
           entry.put(LogParser.END,parser.m_stop);
           parser.m_date = new DateType();
           parser.m_start = new TimeType();
           parser.m_stop = new TimeType();
           parser.log.add(entry);
           state = 2;
           iterator.previous();
           continue;
         }else{
           state = 2;
           buffer += c;
           continue;
         }
        case 2:
         // Capture log
         // look for new line or not number
         if(c == '\n'){
           buffer += c;
           state = 1;
           continue;
         }else{
           buffer += c;
           state = 2;
           continue;
          }
        default:
          break;
      }
    }
    if(buffer.trim().length() > 0){
      if(entry != null){
        entry.put(LogParser.LOG,buffer.trim());
      }else{
        entry = new Hashtable<Object,Object>();
        parser.m_date.setValue("");
        parser.m_start.setValue("");
        parser.m_stop.setValue("");
        entry.put(LogParser.LOG,buffer.trim());
        entry.put(LogParser.DATE,parser.m_date);
        entry.put(LogParser.START,parser.m_start);
        entry.put(LogParser.END,parser.m_stop);
        parser.log.add(entry);
      }
    }
    return parser.log;
  }

  /**
   * Find a date value in a string, save the string in a protected variable.
   * @param iterator Contains string to be parsed
   * @return True if date was found.  False if not.
   * @throws Exception
   */
  protected boolean parseDate(StringCharacterIterator iterator) throws Exception {
    boolean retval = false;
    boolean end = false;
    String buffer = "";
    int state = 1;
    int start_index = iterator.getIndex();
    for(char c = iterator.current();c != CharacterIterator.DONE && end == false;c = iterator.next()){
      switch(state){
        /**
         * Look for number
         */
        case 1:
          if(this.isNumber(c)){
            state = 2;
            buffer = ""+c;
            continue;
          }else{
            state = -1;
            break;
          }
        /**
         * Look for second number
         */
        case 2:
          if(this.isNumber(c)){
            state = 3;
            buffer += c;
            continue;
          }else if(c == '/'){
            state = 4;
            buffer += c;
            continue;
          }else{
            state = -1;
            break;
          }
        /**
         * Look for '/'
         */
        case 3:
          if(c == '/'){
            state = 4;
            buffer += c;
            continue;
          }else{
            state = -1;
            break;
          }
        /**
         * Look for number
         */
        case 4:
          if(this.isNumber(c)){
            state = 5;
            buffer += c;
            continue;
          }else{
            state = -1;
            break;
          }
        /**
         * Look for number
         */
        case 5:
          if(this.isNumber(c)){
            state = 6;
            buffer += c;
            continue;
          }else if(c == '/'){
            state = 7;
            buffer += c;
            continue;
          }else{
            state = -1;
            break;
          }
        /**
         * Look for '/'
         */
        case 6:
          if(c == '/'){
            state = 7;
            buffer += c;
            continue;
          }else{
            state = -1;
            break;
          }
        /**
         * look for number
         */
        case 7:
          if(this.isNumber(c)){
            state = 8;
            buffer +=c;
            continue;
          }else{
            state = -1;
            break;
          }
        /**
         * Look for number
         */
        case 8:
          if(this.isNumber(c)){
            state = 9;
            buffer += c;
            continue;
          }else{
            state = -1;
            break;
          }
        /**
         * Look for number
         */
        case 9:
          if(this.isNumber(c)){
            state = 10;
            buffer += c;
            continue;
          }else if(this.isColon(c)){
            state = 12;
            buffer += ':';
            continue;
          }else{
            state = -1;
            break;
          }
        /**
         * Look for number
         */
        case 10:
          if(this.isNumber(c)){
            state = 11;
            buffer += c;
            continue;
          }else{
            state = -1;
            break;
          }
        /**
         * Look for ':'
         */
        case 11:
          if(this.isColon(c)){
            state = 12;
            //buffer += c;
            iterator.previous();
            continue;
          }else{
            state = -1;
            break;
          }
        /**
         * Save Date
         */
        case 12:
          try{
            this.m_date.setValue(buffer);
            retval = true;
            end = true;
            break;
          }catch(Exception e){
            state = -1;
            break;
          }
        default:
          end = true;
          retval = false;
          break;
      }
    }
    if(retval == false){
      iterator.setIndex(start_index);
    }
    return retval;
  }
  /**
   * Extract a time string.
   * @param iterator The String
   * @return True if it works.  False if not
   * @throws Exception
   */
  protected boolean parseTime(StringCharacterIterator iterator,TimeType time) throws Exception {
    boolean retval = false;
    boolean end = false;
    int state = 1;
    int start_index = iterator.getIndex();
    String buffer = "";
    for(char c = iterator.current();c != CharacterIterator.DONE && end == false;c = iterator.next()){
      switch(state){
        case 1:
          if(this.isNumber(c)){
            state = 2;
            buffer += c;
            continue;
          }else if(this.isWhiteSpace(c)){
            state = 1;
            buffer = "";
            continue;
          }else{
            state = -1;
            break;
          }
        case 2:
          if(this.isNumber(c)){
            state = 3;
            buffer += c;
            continue;
          }else if(this.isColon(c)){
            state = 4;
            buffer += ':';
            continue;
          }else{
            state = -1;
            break;
          }
        case 3:
          if(this.isColon(c)){
            state = 4;
            buffer += ':';
            continue;
          }else{
            state = -1;
            break;
          }
        case 4:
          if(this.isNumber(c)){
            state = 5;
            buffer += c;
            continue;
          }else{
            state = -1;
            break;
          }
        case 5:
          if(this.isNumber(c)){
            state = 6;
            buffer += c;
            continue;
          }else{
            state = -1;
            break;
          }
        case 6:
          if(c == 'a' || c == 'A' || c == 'p' || c == 'P'){
            state = 7;
            buffer += c;
            continue;
          }else if(this.isWhiteSpace(c)){
            state = 6;
            continue;
          }else{
            state = 8;
            continue;
          }
        case 7:
          if(c == 'm' || c == 'M' || this.isWhiteSpace(c) || c == '\n'){
            state = 8;
            if(c == 'm' || c == 'M'){
              buffer += c;
            }
            state = 8;
            iterator.previous();
            continue;
          }else{
            state = -1;
            break;
          }
        case 8:
          try{
            time.setValue(buffer);
            retval = true;
            end = true;
            break;
          }catch(Exception e){
            state = -1;
            break;
          }
        default:
          end = true;
          break;
      }
    }
    if(retval == false){
      iterator.setIndex(start_index);
    }
    return retval;
  }
  /**
   * Is this character a number?
   * @param c
   * @return True if it is a number, False if not.
   */
  protected boolean isNumber(char c){
    if((int)c <= 57 && (int)c >= 48){
      return true;
    }else{
      return false;
    }
  }
  /**
   * Is this character a white space character?
   * @param c Character
   * @return True or False
   */
  protected boolean isWhiteSpace(char c){
    if((int)c <= 32 && (int)c != 10){
      return true;
    }else{
      return false;
    }
  }
  protected boolean parseHeader(StringCharacterIterator iterator) throws Exception {
    boolean success = false;
    boolean datefound = false;
    boolean starttime = false;
    boolean endtime = false;
    boolean end = false;
    char c;

    datefound = this.parseDate(iterator);
    if(datefound == true){
      starttime = this.parseTime(iterator,this.m_start);
    }else{
      success = false;
    }
    if(starttime == true){
      iterator.getIndex();
      int state = 1;
      for(c = iterator.current();c != CharacterIterator.DONE && end == false;c = iterator.next()){
        switch(state){
          case 1:
            if(this.isWhiteSpace(c)){
              state = 1;
              continue;
            }else if(c == '-'){
              iterator.next();
              endtime = this.parseTime(iterator,this.m_stop);
              end = true;
              iterator.previous();
              break;
            }else{
              state = -1;
              //end = true;
              iterator.previous();
              break;
            }
          default:
            iterator.previous();
            end = true;
            break;
        }
      }
    }
    if(endtime == true){
      success = true;
    }else if(starttime == true){
      this.m_stop.setValue("");
      success = true;
    }else if(datefound == true){
      this.m_start.setValue("");
      success = true;
    }else{
      success = false;
    }
    return success;
  }
  protected boolean isColon(char c) {
    if(c == ':' || c == ';'){
      return true;
    }else{
      return false;
    }
  }
}